import { RouteContext } from '@ant-design/pro-layout';
import { ErrorBoundary } from '@ant-design/pro-utils';
import {
  useLocation,
  useNavigate,
  useParams,
  useSearchParams,
} from '@umijs/max';
import { App, Empty, Result, Typography, theme } from 'antd';
import lodash from 'lodash';
import React, { useContext } from 'react';
import '../utils/injectWindow';
import DynamicComponentProProvider from './DynamicComponentProProvider';
import ddeRender, { ReactNodeRenderError } from './ddeRender';

export type DynamicSchema = {
  /**
   * 组件名称
   */
  componentName: string;
  /**
   * 当前节点的id
   */
  id: string;
  /**
   * 动态组件的props
   */
  props: Record<string, any>;
  /**
   * 是否隐藏动态组件
   */
  hidden: boolean;
  /**
   * 动态组件的描述
   */
  title: string;
  /**
   * 子动态组件Schema
   */
  children: undefined | DynamicSchema | DynamicSchema[];
};

export type DynamicComponentProps = {
  /**
   * 动态组件 schema，用于动态渲染
   */
  schema: DynamicSchema | string;
  /**
   * 扩展参数，放置外部传进来的参数
   */
  dcExtParams?: Record<string, any>;
  [key: string]: any;
} & Record<string, any>;

function mergeState<T>(prevState: T, newState: Partial<T>) {
  return { ...prevState, ...newState };
}

const DynamicComponent: React.FC<DynamicComponentProps> = (props) => {
  const {
    schema: schemaProps,
    dcExtParams: dcExtParamsProps,
    ...restProps
  } = props;

  const { token } = theme.useToken();
  const actionRef = React.useRef<any>();
  const formRef = React.useRef<any>();
  const urlParams = useParams();
  const searchParams = useSearchParams();
  const navigate = useNavigate();
  const location = useLocation();
  const routeContext = useContext<any>(RouteContext);
  const backItem = lodash.get(
    routeContext.breadcrumb?.items,
    lodash.size(routeContext.breadcrumb?.items) - 2,
  );
  const app = App.useApp();
  if (!window.sfw.app) {
    window.sfw.app = app;
  }
  const { message, modal, notification } = app;
  const [dcState, setDcStateOriginal] = React.useState<Record<string, any>>({
    // restProps,
    actionRef,
    formRef,
    urlParams: {
      ...urlParams,
      id: lodash.startsWith(urlParams.id, 'docs-') ? null : urlParams.id,
    },
    searchParams,
    location,
    navigate,
    routeContext,
    message,
    modal,
    notification,
    handleBack: () => {
      // 返回上一级页面，从面包屑数据中中获取上级页面配置
      navigate(backItem?.linkPath || -1);
    },
    handleReload: () => {
      navigate(`${location.pathname}${location.search}${location.hash}`, {
        replace: true,
      });
    },
  });
  const setDcState = (newState: Record<string, any>) => {
    setDcStateOriginal((prevState) => mergeState(prevState, newState));
  };
  const dcExtParams = {
    ...dcExtParamsProps,
    dcState,
    setDcState,
    restProps,
    actionRef,
    formRef,
    location,
    navigate,
    routeContext,
    message,
    modal,
    notification,
    handleBack: () => {
      // 返回上一级页面，从面包屑数据中中获取上级页面配置
      navigate(backItem?.linkPath || -1);
    },
    handleReload: () => {
      navigate(`${location.pathname}${location.search}${location.hash}`, {
        replace: true,
      });
    },
  };

  const toJson = (schemaProps: string) => {
    if (!schemaProps) {
      return schemaProps;
    }
    try {
      return JSON.parse(schemaProps);
    } catch (e) {
      throw new ReactNodeRenderError(
        `Schema 不是一个正确的 JSON`,
        `$`,
        lodash.toString(schemaProps),
      );
    }
  };
  const toDom = (schema: object) => {
    try {
      return ddeRender.reactNodeRender(
        token,
        `$`,
        schema,
        dcExtParams,
        restProps,
      );
    } catch (e) {
      if (e instanceof ReactNodeRenderError) {
        throw e;
      }
      throw new ReactNodeRenderError(`组件渲染错误。${e}`, `$`, schema);
    }
  };

  try {
    const schema = lodash.isString(schemaProps)
      ? toJson(schemaProps)
      : schemaProps;
    if (!schema) {
      return <Empty description="Schema 为空" />;
    }

    return (
      <ErrorBoundary>
        <DynamicComponentProProvider>
          {toDom(schema)}
        </DynamicComponentProProvider>
      </ErrorBoundary>
    );
  } catch (e: any | ReactNodeRenderError) {
    return (
      <Result
        status="error"
        title={
          // `字段：${e.fieldPath}，错误：${e.message}`
          <>
            字段：
            <Typography.Text style={{ fontSize: 'inherit' }} code copyable>
              {e.fieldPath}
            </Typography.Text>
            <br />
            <Typography.Text style={{ fontSize: 'inherit' }} type="danger">
              {e.message}
            </Typography.Text>
          </>
        }
        subTitle={
          <Typography.Text type={'secondary'} copyable>
            {lodash.isObject(e.schema)
              ? JSON.stringify(e.schema)
              : lodash.toString(e.schema)}
          </Typography.Text>
        }
      />
    );
  }
};

export default DynamicComponent;
